package com.wan.data.mysql.sql;

import cn.hutool.core.util.StrUtil;
import com.wan.data.mysql.exception.DBInitializeException;
import com.wan.data.mysql.orm.annotion.Index;
import com.wan.data.mysql.orm.annotion.Table;
import com.wan.data.mysql.orm.convert.Converter;
import com.wan.data.mysql.orm.meta.ColumnMeta;
import com.wan.data.mysql.orm.meta.IndexMeta;
import com.wan.data.mysql.orm.meta.TableMeta;
import com.wan.data.mysql.util.AlterType;
import com.wan.setting.Debug;
import com.wan.util.SplitUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 创库、创表、修复表结构等
 * 启动执行一次，故全静态方法
 */
public class DDLSql {
    public static final String COLLATE_DEFAULT = "utf8mb4_bin";
    public static final String CHARSET_DEFAULT = "utf8mb4";

    /**
     * 创建数据库
     */
    public static String createDbSql(String dbName){
        String sql = "create database if not exists %s CHARACTER SET %s collate %s";
        return String.format(sql,dbName,CHARSET_DEFAULT,COLLATE_DEFAULT);
    }

    /**
     * 创建表
     */
    public static String createTableSql(TableMeta tableMeta){
        //创建表sql                              -表名  -表字段              -主键    -索引       -引擎           -自增起始值             -编码
        String sql = "CREATE TABLE IF NOT EXISTS `%s` (\n%s,\n PRIMARY KEY (`%s`),\n %s\n)ENGINE=%s AUTO_INCREMENT=%s DEFAULT CHARSET=%s";
        ColumnMeta pkColumn = tableMeta.getPkColumn();
        List<String> columnArr = new ArrayList<>();
        columnArr.add(buildColumnDefine(pkColumn));
        for (ColumnMeta columnMeta : tableMeta.getColumnMetas()) {
            if(columnMeta.isPk()){
                continue;
            }
            columnArr.add(buildColumnDefine(columnMeta));
        }
        String columnDefine = SplitUtil.concat(columnArr,SplitUtil.COMMA_SEPARATOR + "\n");
        Set<String> indexArr = new HashSet<>();
        for (IndexMeta indexColumnMeta : tableMeta.getIndexColumnMetas()) {
            indexArr.add(buildIndexDefine(indexColumnMeta));
        }
        String indexDefine = SplitUtil.concat(indexArr,SplitUtil.COMMA_SEPARATOR + "\n");
        return String.format(sql, tableMeta.getTableName(), columnDefine, pkColumn.getColumnName(),
                indexDefine, Table.ENGINE_DEFAULT, tableMeta.getAutoBegin(), CHARSET_DEFAULT);
    }

    /**
     * 查询表结构信息
     */
    public static String queryColumnsInfoSql(TableMeta tableMeta){
        String sql = "SELECT `COLUMN_NAME`,`IS_NULLABLE`,`COLUMN_TYPE`,`EXTRA`,`COLUMN_COMMENT`,`COLUMN_KEY` FROM " +
                "`information_schema`.`COLUMNS` where TABLE_SCHEMA= '%s' and TABLE_NAME = '%s'";
        return String.format(sql,tableMeta.getDatabaseName(),tableMeta.getTableName());
    }

    /**
     * 索引信息
     */
    public static String showIndex(TableMeta tableMeta){
        String sql = "show index from `%s`.`%s`";
        return String.format(sql,tableMeta.getDatabaseName(),tableMeta.getTableName());
    }

    /**
     * 组建修改表结构的sql
     * @param columnAlters 字段变更
     * @param alterMap 索引变更
     * @param newPrimary 新主键
     */
    public static String alterTableSql(TableMeta tableMeta, Map<AlterType,List<ColumnMeta>> columnAlters,
                                       Map<AlterType,List<IndexMeta>> alterMap, String newPrimary){
        String sql = "ALTER TABLE `%s`.`%s` \n%s";
        List<String> defineList = new ArrayList<>();
        for (Map.Entry<AlterType, List<ColumnMeta>> entry : columnAlters.entrySet()) {
            AlterType alterType = entry.getKey();
            for (ColumnMeta columnMeta : entry.getValue()) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(alterType.name()).append(" COLUMN ");
                if(alterType == AlterType.DROP){
                    stringBuilder.append(" `").append(columnMeta.getColumnName()).append("` ");
                }else{
                    stringBuilder.append(buildColumnDefine(columnMeta));
                }
                defineList.add(stringBuilder.toString());
            }
        }
        for (Map.Entry<AlterType, List<IndexMeta>> entry : alterMap.entrySet()) {
            AlterType alterType = entry.getKey();
            for (IndexMeta indexMeta : entry.getValue()) {
                switch (alterType){
                    case ADD:
                        defineList.add(alterType.name() + buildIndexDefine(indexMeta));
                        break;
                    case DROP:
                        defineList.add(alterType.name() + " index `" + indexMeta.getName() + "`");
                        break;
                    case MODIFY:
                        defineList.add(AlterType.DROP.name() + " index `" + indexMeta.getName() + "`");
                        defineList.add(AlterType.ADD.name() +  buildIndexDefine(indexMeta));
                        break;
                }
            }
        }
        //更换主键,暂时只支持单主键模式,多主键暂不考虑,仅debug模式支持
        if(Debug.isDebug() && newPrimary != null){
            defineList.add("DROP PRIMARY KEY");
            defineList.add("ADD PRIMARY KEY (" + newPrimary + ")");
        }
        if(defineList.isEmpty()){
            return null;
        }
        String content = SplitUtil.concat(defineList,",\n");
        return String.format(sql,tableMeta.getDatabaseName(),tableMeta.getTableName(),content);
    }


    /**
     * 字段定义
     */
    private static String buildColumnDefine(ColumnMeta columnMeta){
        Converter converterInstance = Converter.get(columnMeta);
        StringBuilder builder = new StringBuilder("`" + columnMeta.getColumnName() + "` ");
        builder.append(converterInstance.dbType(columnMeta.getLength(),columnMeta.getScale()));
        if(columnMeta.isNotNull() || columnMeta.isPk()){
            builder.append(" NOT NULL ");
        }
        if(columnMeta.isAutoIncrement()){
            builder.append(" AUTO_INCREMENT ");
        }
        if(StrUtil.isNotEmpty(columnMeta.getComment())){
            builder.append(" COMMENT '").append(columnMeta.getComment()).append("' ");
        }
        return builder.toString();
    }

    /**
     * 索引定义
     */
    private static String buildIndexDefine(IndexMeta indexMeta){
        if(indexMeta.getColumnMetas().isEmpty()){
            throw new DBInitializeException("索引指定的列为空,index:" + indexMeta.getName());
        }
        String concat = SplitUtil.concat(indexMeta.getColumnMetas().stream()
                .map(c-> "`" + c.getColumnName() + "`").collect(Collectors.toList()));
        String define = " INDEX `" + indexMeta.getName() + "`" + "(" + concat + ")";
        return indexMeta.getType().equals(Index.INDEX_NORMAL) ? define : " " + indexMeta.getType() + define;
    }

}
